home *** CD-ROM | disk | FTP | other *** search
/ Mac Expert 1995 Winter / Mac Expert - Winter 95.iso / Les fichiers / Communications / Internet / TurboTCP 2.1 ƒ / PP MiniTelnet source / CTelnetTerminal.cp < prev    next >
Encoding:
Text File  |  1995-01-19  |  13.5 KB  |  698 lines  |  [TEXT/MMCC]

  1. //
  2. // CTelnetTerminal.cp
  3. //
  4. //    MiniTelnet application
  5. //    Telnet terminal session document
  6. //    PowerPlant version
  7. //
  8. //    Copyright © 1993-95, FrostByte Design / Eric Scouten
  9. //
  10.  
  11. #include "CTelnetTerminal.h"
  12.  
  13. #include <LWindow.h>
  14.  
  15. #include "CTerminalPane.h"
  16.  
  17.  
  18.  
  19. //    —— constructor ——
  20.  
  21. //***********************************************************
  22.  
  23. /*______________________________________________________________________
  24. **
  25. ** constructor
  26. **
  27. **    Initialize the Telnet terminal session document.
  28. **
  29. **        theDefaultPort (unsigned short):    default IP port number
  30. **        recBufferSize (unsigned long):        size of the receive buffer we need
  31. **        autoReceiveSize (unsigned long):    number of entries in RDS for auto-receive,
  32. **                                        0 to disable autoreceiving
  33. **        autoReceiveNum (unsigned short):    number of auto-receive calls to issue at once
  34. **                                        must be at least 1
  35. **        doUseCName (unsigned Boolean):    TRUE to look up canonical names for hosts
  36. **        printable (Boolean):                TRUE if document is printable
  37. **
  38. */
  39.  
  40.  
  41.  
  42.  
  43. //    —— creating new sessions ——
  44. //***********************************************************
  45.  
  46. /*______________________________________________________________________
  47. **
  48. ** NewSession
  49. **
  50. **    Create a new window for document. Pulls information from the settings record which
  51. **    it receives and opens a session accordingly.
  52. **
  53. **        theSettings (TelnetSettingsRec*):    the settings record to use
  54. **
  55. */
  56.  
  57.  
  58.  
  59. //***********************************************************
  60.  
  61.  
  62. /*______________________________________________________________________
  63. **
  64. ** BuildWindow
  65. **
  66. **    Build a window for a terminal connection.
  67. **
  68. */
  69.  
  70.  
  71.  
  72.  
  73. // —— closing windows & sessions ——
  74.  
  75. // -- closing windows & sessions --
  76.  
  77. //***********************************************************
  78.  
  79. /*______________________________________________________________________
  80. **
  81. ** Close
  82. **
  83. **    Respond to user close. Ensure that the TCP stream is gracefully closed. (This method is
  84. **    also called if the remote host closes the session or the session is terminated.)
  85. **
  86. **        quitting (Boolean):    TRUE if quitting
  87. **
  88. **        return (Boolean):    FALSE if close/quit aborted by user
  89. **
  90. */
  91.  
  92.  
  93.  
  94. //***********************************************************
  95.  
  96. /*______________________________________________________________________
  97. **
  98. ** RemoteClose
  99. **
  100. **    Respond to notification that the remote host cancelled the session (either by normal
  101. **    close or terminate).
  102. **
  103. */
  104.  
  105. void CTelnetTerminal::RemoteClose()
  106.  
  107. {
  108.     Boolean sessionWasEstablished = SessionEstablished();
  109.  
  110.     CTelnetInterp::RemoteClose();
  111.     if ((goAwayOnClose) && (!sessionWasEstablished))
  112.         LDocument::Close();
  113. }
  114.  
  115.  
  116. //    —— window titling ——
  117. //***********************************************************
  118.  
  119. /*______________________________________________________________________
  120. **
  121. ** SetWindowTitle
  122. **
  123. **    Sets title for window. Turns on or off cursor blinking depending on whether session
  124. **    is established.
  125. **
  126. **        newTitle (Str255):    the new title for the window
  127. **
  128. */
  129.  
  130. void CTelnetTerminal::SetWindowTitle(Str255 newTitle)
  131.  
  132. {
  133.     if (mWindow)
  134.         mWindow->SetDescriptor(newTitle);
  135.     itsTerminal->SetBlinking(SessionEstablished());
  136. }
  137.  
  138.  
  139. //    —— command/event handling ——
  140. //***********************************************************
  141.  
  142. /*______________________________________________________________________
  143. **
  144. ** DoCommand
  145. **
  146. **    Handle all commands document can understand.
  147. **
  148. **        theCommand (long):    command number that was issued
  149. **
  150. */
  151.  
  152.  
  153.  
  154. //***********************************************************
  155.  
  156. /*______________________________________________________________________
  157. **
  158. ** DoKeyDown
  159. **
  160. **    Process key-down events. Parses CR, LF codes and generates the
  161. **    standard CR/LF sequence when CR is passed and ignores LF.
  162. **
  163. **        theChar (char):                the character that was entered
  164. **        keyCode (byte):            the Mac ADB key number for the key that was pressed
  165. **        macEvent (EventRecord*):    the entire event record
  166. **
  167. */
  168.  
  169.  
  170.  
  171. //***********************************************************
  172.  
  173. /*______________________________________________________________________
  174. **
  175. ** DoPaste
  176. **
  177. **    Respond to Paste command by sending all characters from the
  178. **    clipboard to the remote host.
  179. **
  180. */
  181.  
  182. /*
  183. void CTelnetTerminal::DoPaste()
  184.  
  185. {
  186.     Handle    theData;
  187.     char        lineBfr[83];
  188.     char        *p, *pl, *lb;
  189.     long        rem;
  190.     short    i;
  191.     
  192.     // get the clipboard contents
  193.     
  194.     if (!(gClipboard->GetData('TEXT', &theData)))
  195.         return;
  196.     HLock(theData);
  197.     
  198.     // send it in line or 80-char chunks
  199.     
  200.     p = (char*) *theData;
  201.     rem = GetHandleSize(theData);
  202.     
  203.     while (rem > 0) {
  204.     
  205.         // fetch a line (or 81 chars)
  206.         
  207.         i = 0;
  208.         pl = p;
  209.         lb = lineBfr;
  210.         while ((rem-(i) > 0) && (*pl != charCR) && (i < 80))
  211.             i++, *lb++ = *pl++;
  212.         if (*pl == charCR) {
  213.             i++, *pl++;
  214.             *lb++ = charCR;
  215.             *lb++ = charLF;
  216.         }
  217.         *lb = '\0';
  218.     
  219.         // send the line
  220.         
  221.         #if _TestTerminal
  222.             HandleNVTLine(lineBfr);
  223.         #else
  224.             *this << lineBfr;
  225.         #endif
  226.     
  227.         rem -= i;
  228.         p += i;
  229.         
  230.     }
  231.     
  232.     HUnlock(theData);
  233.     DisposHandle(theData);
  234. }
  235. */
  236.  
  237.  
  238. //***********************************************************
  239.  
  240. /*______________________________________________________________________
  241. **
  242. ** UpdateMenus
  243. **
  244. **    Enable Telnet-specific commands.
  245. **
  246. */
  247.  
  248.  
  249.  
  250.  
  251. //    —— Telnet command handling ——
  252. //***********************************************************
  253.  
  254. /*______________________________________________________________________
  255. **
  256. ** ReceivedDo
  257. **
  258. **    Respond to a Telnet [IAC DO option] sequence.
  259. **
  260. **        theOption (uchar):    the option code
  261. **
  262. */
  263.  
  264. void CTelnetTerminal::ReceivedDo(uchar theOption)
  265.  
  266. {
  267.     char respondStr[4];
  268.     
  269.     // parse the option and respond to it
  270.     
  271.     switch (theOption) {
  272.     
  273.         // accept terminal type option only
  274.         
  275.         case optTERMINAL_TYPE:
  276.             respondStr[0] = charIAC;
  277.             respondStr[1] = escWILL;
  278.             respondStr[2] = theOption;
  279.             respondStr[3] = '\0';
  280.             if (showDebug) {
  281.                 PrintDebugStr("{IAC WILL");
  282.                 PrintDebugCharNum(theOption, ' ', '}');
  283.             }
  284.             *this << respondStr;
  285.             break;
  286.         
  287.         // reject the remaining options
  288.         
  289.         default:
  290.             CTelnetInterp::ReceivedDo(theOption);
  291.     }
  292.  
  293. }
  294.  
  295. //***********************************************************
  296.  
  297. /*______________________________________________________________________
  298. **
  299. ** ReceivedAYT
  300. **
  301. **    Respond to a Telnet [IAC AYT] sequence.
  302. **
  303. */
  304.  
  305. void CTelnetTerminal::ReceivedAYT()
  306.  
  307. {
  308.     if (showDebug)
  309.         PrintDebugStr("{Yes}");
  310.     *this << "[Yes]";
  311. }
  312.  
  313. //***********************************************************
  314.  
  315. /*______________________________________________________________________
  316. **
  317. ** ReceivedSE
  318. **
  319. **    Parse subnegotiation parameters. Called when [IAC SE] is received.
  320. **
  321. */
  322.  
  323. void CTelnetTerminal::ReceivedSE()
  324.  
  325. {
  326.     // dispatch to an option-parsing routine
  327.     
  328.     if (sbBfrIndex < 1)
  329.         return;
  330.     switch (sbBfr[0]) {
  331.  
  332.         case optTERMINAL_TYPE:
  333.             OptionTerminalType();
  334.             break;
  335.         
  336.         default:
  337.             CTelnetInterp::ReceivedSE();
  338.     }
  339.     
  340. }
  341.  
  342.  
  343. //    —— Telnet option handling ——
  344. //***********************************************************
  345.  
  346. /*______________________________________________________________________
  347. **
  348. ** OptionTerminalType
  349. **
  350. **    Interpret an [IAC SB TERMINAL-TYPE ... IAC SE] sequence. Default method always says
  351. **    this is an unknown terminal.
  352. **
  353. */
  354.  
  355. void CTelnetTerminal::OptionTerminalType()
  356.  
  357. {
  358.     char        respondStr[47];
  359.     short    i = 2;
  360.     
  361.     // respond only to TERMINAL-TYPE SEND queries; ignore others
  362.     
  363.     if (sbBfr[1] == 1) {
  364.  
  365.         // build a response string
  366.         
  367.         respondStr[0] = charIAC;
  368.         respondStr[1] = escSB;
  369.         respondStr[2] = optTERMINAL_TYPE;
  370.         
  371.         // switch to next terminal emulation
  372.         
  373.         itsTermMode++;
  374.         if (itsTermMode > termMax)
  375.             itsTermMode = 0;
  376.         // SetTerminalMode(itsTermMode);
  377.         
  378.         // get name of terminal emulation
  379.         
  380.         GetTerminalName(itsTermMode, &respondStr[3]);
  381.         
  382.         // display response if debugging mode enabled
  383.                 
  384.         if (showDebug) {
  385.             PrintDebugStr("{IAC SB TERM-TYPE IS ");
  386.             PrintDebugStr(&respondStr[3]);
  387.             PrintDebugStr(" IAC SE}");
  388.         }
  389.         
  390.         // add end of SB string & send it
  391.         
  392.         while (respondStr[++i])
  393.             ;
  394.         respondStr[i] = charIAC;
  395.         respondStr[i+1] = escSE;
  396.         respondStr[i+2] = '\0';
  397.         *this << respondStr;
  398.     }
  399.     
  400. }
  401.  
  402.  
  403. //    —— terminal emulation handling ——
  404. //***********************************************************
  405.  
  406. /*______________________________________________________________________
  407. **
  408. ** GetTerminalName
  409. **
  410. **    Return the Internet assigned name for the terminal being used.
  411. **
  412. **        termIndex (short):    the terminal emulation number
  413. **        termStr (char*):    buffer to receive the terminal name (max 40 chars)
  414. **
  415. */
  416.  
  417. void CTelnetTerminal::GetTerminalName(short termIndex, char* termStr)
  418.  
  419. {
  420.     char theTerm[41] = "UNKNOWN";        // for now, all we have is “UNKNOWN”
  421.     ::BlockMoveData(theTerm, termStr, 41);
  422. }
  423.  
  424. void CTelnetTerminal::AttemptClose(Boolean inRecordIt)
  425. {
  426.     if (LocalClose(false))
  427.         LDocument::AttemptClose(inRecordIt);
  428. }
  429.  
  430.  
  431. void CTelnetTerminal::FindCommandStatus(CommandT inCommand,
  432.                         Boolean& outEnabled, Boolean& outUsesMark,
  433.                         Char16& outMark, Str255 outName)
  434. {
  435.     switch (inCommand) {
  436.     
  437.         // Edit menu: support pasting TEXT
  438. /*
  439.         case cmdPaste:
  440.             DoPaste();
  441.             break;
  442. */
  443.         
  444.         // Telnet menu: send various strings to server
  445.         
  446.         case cmd_SendSynch:
  447.         case cmd_SendBreak:
  448.         case cmd_SendAO:
  449.         case cmd_SendIP:
  450.         case cmd_SendAYT:
  451.         case cmd_SendGA:
  452.         case cmd_SendEC:
  453.         case cmd_SendEL:
  454.         case cmd_SendIPAddr:
  455.         case cmd_ShowDebug:
  456.             outEnabled = SessionEstablished();
  457.             break;
  458.  
  459.         // not ours, send along the chain
  460.         
  461.         default:
  462.             LDocument::FindCommandStatus (inCommand, outEnabled, outUsesMark, outMark, outName);
  463.  
  464.     }
  465.     
  466.  
  467.     // enable “Paste” command only if valid Clipboard
  468. /*
  469.     #if _TestTerminal
  470.         if (gClipboard->DataSize('TEXT'))
  471.             gBartender->EnableCmd(cmdPaste);
  472.     #else
  473.         if (SessionEstablished() && (gClipboard->DataSize('TEXT')))
  474.             gBartender->EnableCmd(cmdPaste);
  475.     #endif
  476. */
  477.  
  478. }
  479.  
  480.  
  481. Boolean CTelnetTerminal::HandleKeyPress(const EventRecord& inKeyEvent)
  482.  
  483. {
  484.     unsigned char theChar = inKeyEvent.message & 0xFF;
  485.  
  486.  
  487.     // make sure this isn’t an errant command key
  488.     
  489.     if (!(inKeyEvent.modifiers & cmdKey)) {
  490.         if (theChar != 0x0A) {                        // TEMPORARY: we'll do constants later…
  491.             #if _TestTerminal
  492.                 HandleNVTChar(theChar);
  493.                 if (theChar == 0x0D)
  494.                     HandleNVTChar(0x0A);
  495.             #else
  496.                 if (!SessionEstablished()) {
  497.                     ::SysBeep(0);
  498.                     return true;
  499.                 }
  500.         
  501.                 if (theChar == 0x08)
  502.                     SendChar(mSettings.backspaceChar);
  503.                 else
  504.                     SendChar(theChar);
  505.         
  506.                 if (theChar == 0x0D)
  507.                     SendChar(0x0A);
  508.             #endif
  509.         itsTerminal->ScrollToSelection();
  510.         }
  511.     }
  512.     else
  513.         return LCommander::HandleKeyPress(inKeyEvent);
  514.  
  515.     return true;
  516.  
  517. }
  518.  
  519.  
  520. void CTelnetTerminal::HandleNVTChar(uchar theChar)
  521.  
  522. {
  523.     itsTerminal->DoWriteChar(theChar);
  524. }
  525.  
  526.  
  527. void CTelnetTerminal::HandleNVTLine(char* theLine)
  528. {
  529.     itsTerminal->DoWriteStr(theLine);
  530. }
  531.  
  532.  
  533. void CTelnetTerminal::PrintDebugCharNum(char theChar, char leftBracket, char rightBracket)
  534. {
  535.     itsTerminal->DoWriteCharNum(theChar, leftBracket, rightBracket);
  536. }
  537.  
  538.  
  539. void CTelnetTerminal::ReceivedEL()
  540. {
  541.     itsTerminal->DoEraseLine();
  542. }
  543.  
  544.  
  545. void CTelnetTerminal::ReceivedEC()
  546. {
  547.     itsTerminal->DoEraseChar();
  548. }
  549.  
  550.  
  551. void CTelnetTerminal::PrintDebugStr(char* theDebugStr)
  552. {
  553.     itsTerminal->DoWriteStr(theDebugStr);
  554. }
  555.  
  556.  
  557. Boolean CTelnetTerminal::ObeyCommand(CommandT inCommand, void* ioParam)
  558.  
  559. {
  560.     switch (inCommand) {
  561.     
  562.         // Edit menu: support pasting TEXT
  563. /*
  564.         case cmdPaste:
  565.             DoPaste();
  566.             break;
  567. */
  568.         
  569.         // Telnet menu: send various strings to server
  570.         
  571.         case cmd_SendSynch:
  572.             if (showDebug)
  573.                 PrintDebugStr("{Urgent IAC DM}");
  574.             SetNextUrgent();
  575.             *this << "\377\362";
  576.             break;
  577.  
  578.         case cmd_SendBreak:
  579.             if (showDebug)
  580.                 PrintDebugStr("{Urgent IAC BRK IAC DM}");
  581.             SetNextUrgent();
  582.             *this << "\377\363\377\362";
  583.             break;
  584.             
  585.         case cmd_SendAO:
  586.             if (showDebug)
  587.                 PrintDebugStr("{Urgent IAC AO IAC DM}");
  588.             SetNextUrgent();
  589.             *this << "\377\365\377\362";
  590.             break;
  591.             
  592.         case cmd_SendIP:
  593.             if (showDebug)
  594.                 PrintDebugStr("{Urgent IAC IP IAC DM}");
  595.             SetNextUrgent();
  596.             *this << "\377\364\377\362";
  597.             break;
  598.             
  599.         case cmd_SendAYT:
  600.             if (showDebug)
  601.                 PrintDebugStr("{Urgent IAC AYT IAC DM}");
  602.             SetNextUrgent();
  603.             *this << "\377\366\377\362";
  604.             break;
  605.             
  606.         case cmd_SendGA:
  607.             if (showDebug)
  608.                 PrintDebugStr("{IAC GA}");
  609.             *this << "\377\371";
  610.             break;
  611.             
  612.         case cmd_SendEC:
  613.             if (showDebug)
  614.                 PrintDebugStr("{IAC EC}");
  615.             *this << "\377\367";
  616.             break;
  617.             
  618.         case cmd_SendEL:
  619.             if (showDebug)
  620.                 PrintDebugStr("{IAC EL}");
  621.             *this << "\377\370";
  622.             break;
  623.             
  624.         case cmd_SendIPAddr:
  625.             *this << local_IP;
  626.             break;
  627.  
  628.         case cmd_ShowDebug:
  629.             showDebug = !showDebug;
  630.             mSettings.showDebug = showDebug;
  631.             break;
  632.             
  633.         // not ours, send along the chain
  634.         
  635.         default:
  636.             return LDocument::ObeyCommand(inCommand, ioParam);
  637.     }
  638.     
  639.     return true;
  640.     
  641. }
  642.  
  643.  
  644. void CTelnetTerminal::NewSession(TelnetSettingsRec& inSettings)
  645.  
  646. {
  647.     // copy the settings record
  648.     
  649.     ::BlockMoveData(&inSettings, &mSettings, sizeof (TelnetSettingsRec));
  650.     ::BlockMoveData(mSettings.hostName, hostCName, 256);
  651.     goAwayOnClose = mSettings.closeOnSessionEnd;
  652.     showDebug = mSettings.showDebug;
  653.     
  654.  
  655.     // build a window immediately
  656.     
  657.     mWindow = LWindow::CreateWindow(PPobTelnet, this);
  658.     ThrowIfNil_(mWindow);
  659.     itsTerminal = (CTerminalPane*) mWindow->FindPaneByID('TERM');        //! TEMPORARY: constants later
  660.     ThrowIfNil_(itsTerminal);
  661.     StateChanged();
  662.     
  663.  
  664.     // open host by name
  665.     
  666.     #if _TestTerminal
  667.         itsTerminal->DoWriteStr("No TCP session. Just type onto terminal.\mSettings\n");
  668.     #else
  669.         SetOpenTimeout(20);
  670.         OpenUserHost((char*) mSettings.hostName, defaultPort, TRUE);
  671.     #endif
  672.  
  673. }
  674.  
  675.  
  676. void CTelnetTerminal::GetFileName(Str255 theName)
  677. {
  678.     GetDescriptor(theName);
  679. }
  680.  
  681.  
  682. CTelnetTerminal::CTelnetTerminal(LCommander* inSuper,
  683.                         unsigned short theDefaultPort,
  684.                         unsigned long recBufferSize ,
  685.                         unsigned short autoReceiveSize ,
  686.                         unsigned short autoReceiveNum ,
  687.                         Boolean doUseCName )
  688. : LSingleDoc(inSuper),
  689.   CTelnetInterp(theDefaultPort, recBufferSize, autoReceiveSize, autoReceiveNum, doUseCName)
  690.  
  691. {
  692.     itsTerminal = nil;
  693.     itsTermMode = 0;
  694.     showFileName = false;
  695. }
  696.  
  697.  
  698.